home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1996 March / EnigmA AMIGA RUN 05 (1996)(G.R. Edizioni)(IT)[!][issue 1996-03][Skylink CD IV].iso / earcd / comm2 / alist.lha / src / alconfig.e < prev    next >
Text File  |  1995-11-08  |  27KB  |  875 lines

  1. /* ALConfig.M */
  2.  
  3. OPT MODULE
  4. OPT EXPORT
  5.  
  6. /* Module to load the global config file, parse it, act on it, then load the list config files */
  7.  
  8. MODULE 'dos/datetime'
  9. MODULE 'dos/dos'
  10. MODULE 'dos/dosasl'
  11. MODULE 'dos/var'
  12. MODULE 'other/allog'
  13. MODULE 'other/aladd'
  14. MODULE 'other/alcmd'
  15.  
  16. /* Mailing List Entry */
  17. OBJECT config_node
  18.    next:PTR TO config_node
  19.    name:PTR TO CHAR                 /* Unique list name (filename - '.list') */
  20.    owner:PTR TO CHAR                /* Owner's address */
  21.    password:PTR TO CHAR             /* Password needed to administer list & change config */
  22.    subject:PTR TO CHAR              /* String to ensure is in the subject line */
  23.    flags                            /* CNF_* */
  24.    approved:PTR TO CHAR             /* Approved: password */
  25.    sdesc:PTR TO CHAR                /* One-Line description of list */
  26.    ldesc:PTR TO CHAR                /* Multi-line description of list */
  27.    footer:PTR TO CHAR               /* Multi-line footer to tack onto the end of messages */
  28.    header:PTR TO CHAR               /* Multi-line headers to insert into header group */
  29.    file_get_pwd:PTR TO CHAR         /* Password required for file gets */
  30.    file_put_pwd:PTR TO CHAR         /* Password required for file posts */
  31.    welcome:PTR TO CHAR              /* Multi-line welcome text appended to default welcome message */
  32.    decrypt:PTR TO CHAR              /* Command to use to decrypt incoming messages */
  33.    encrypt:PTR TO CHAR              /* Command to use to encrypt outgoing messages */
  34.    keep_headers:PTR TO CHAR         /* Multi-line list of headers to keep.  NIL if 'none' or 'all' */
  35.    digest:PTR TO digest             /* Digest structure, or NIL for no digest */
  36.    archive_period                   /* AP_* (should be :CHAR, but there's nothing to offset it) */
  37.    users:PTR TO CHAR                /* Chain of users on the list */
  38. ENDOBJECT
  39.  
  40. /* Values for archive_period */
  41. ENUM AP_NONE, AP_DAILY, AP_WEEKLY, AP_MONTHLY, AP_YEARLY
  42.  
  43. /* Flags for the config_node.flags field */
  44. SET CNF_AUTOCMD_FWD,    /* Forward 'posted' commands to owner */
  45.    CNF_AUTOCMD_USE,     /* Process 'posted' commands */
  46.    CNF_CMP_DOMAIN,      /* Only compare domain name and user name */
  47.    CNF_MOD_LIST_SELF,   /* ModerateList: self */
  48.    CNF_MOD_LIST_OWN,    /* ModerateList: owner */
  49.    CNF_MOD_POSTS_SELF,  /* ModeratePosts: self */
  50.    CNF_MOD_POSTS_OWN,   /* ModeratePosts: owner */
  51.    CNF_MOD_FILES_SELF,  /* ModerateFiles: self */
  52.    CNF_MOD_FILES_OWN,   /* ModerateFiles: owner */
  53.    CNF_MOD_FPOST_SELF,  /* ModerateFPosts: self */
  54.    CNF_MOD_FPOST_OWN,   /* ModerateFPosts: owner */
  55.    CNF_MOD_INFO_SELF,   /* ModerateInfo: self */
  56.    CNF_MOD_INFO_OWN,    /* ModerateInfo: owner */
  57.    CNF_MOD_WHO_SELF,    /* ModerateWho: self */
  58.    CNF_MOD_WHO_OWN,     /* ModerateWho: owner */
  59.    CNF_INVISIBLE,       /* Don't show on 'INDEX' output */
  60.    CNF_ENCRYPT_EACH,    /* '$TO' or '$KEY' is part of encrypt command string; call for each member */
  61.    CNF_KEEP_RECEIVED,   /* Keep all 'Received:' headers */
  62.    CNF_KEEP_HEADERS,    /* Keep all headers (excluding 'Received:' if CNF_KEEP_RECEIVED is false) */
  63.    CNF_REQUIRE_KEY      /* Require a key for every user on the list */
  64.  
  65. /* Structure to add if there is a digest for a list */
  66. /* Note that it must be a known list */
  67. OBJECT digest
  68.    name:PTR TO CHAR     /* Name of another valid list to use */
  69.    header:PTR TO CHAR   /* Multi-Line list of headers to add when posting a digest */
  70.    footer:PTR TO CHAR   /* Multi-Line divider between posts */
  71.    iname:PTR TO CHAR    /* Name for 'Issue' */
  72.    vname:PTR TO CHAR    /* Name for 'Volume', or NIL for no volume */
  73.    issue:INT            /* Starting issue # */
  74.    volume:INT           /* Starting volume #, if used */
  75.    lines:INT            /* Number of lines before triggering post */
  76.    size:INT             /* Number of K before triggering post */
  77.    age:INT              /* Number of days before triggering post */
  78.    i_v:INT              /* Number of issues per volume; ignored if vname=NIL */
  79.    time:PTR TO datetime /* DateStamp of first post to the current volume/issue */
  80.    current_lines:LONG   /* Running # of lines in the current issue */
  81.    current_size:LONG    /* Running size in bytes of the current issue */
  82. ENDOBJECT
  83.  
  84.  
  85. /* These are defined in alist.e, but used here too... */
  86. DEF list_dir, files_dir, config, password:PTR TO CHAR, config_list:PTR TO config_node, hostname:PTR TO CHAR
  87.  
  88.  
  89. /*
  90.  * Load the global config file and act on it, then close 'config'
  91.  */
  92. PROC parse_config()
  93.    DEF str:PTR TO CHAR, x
  94.  
  95.    str := String (80)
  96.    WHILE (Fgets (config, str, 80))
  97.       x := StrLen (str)
  98.       IF (str[x-1] = "\n")
  99.          str[x-1] := 0
  100.          DEC x
  101.       ENDIF
  102.       SetStr (str, x)
  103.  
  104.       IF (str[0] = "#") OR (str[0] < 33)
  105.          /* Ignore it, it's a comment */
  106.       ELSEIF (StrCmp (str, 'ListDir', 7))
  107.          IF (list_dir)
  108.             log_message ('Successive ListDir directives ignored.\n', LOG_WARNING)
  109.          ELSE
  110.             x := TrimStr (str+8)
  111.             IF (x <> 0)  THEN list_dir := Lock (x, ACCESS_READ)
  112.             IF (list_dir = NIL)
  113.                log_message ('Unable to open requested ListDir.\n', LOG_WARNING)
  114.             ENDIF
  115.          ENDIF
  116.       ELSEIF (StrCmp (str, 'FilesDir', 8))
  117.          IF (files_dir)
  118.             log_message ('Successive FilesDir directives ignored.\n', LOG_WARNING)
  119.          ELSE
  120.             x := TrimStr (str+9)
  121.             IF (x <> 0)  THEN files_dir := Lock (x, ACCESS_READ)
  122.             IF (files_dir = NIL)  THEN log_message ('Unable to open requested FilesDir.\n', LOG_WARNING)
  123.          ENDIF
  124.       ELSEIF (StrCmp (str, 'Password', 8))
  125.          IF (password)
  126.             log_message ('Successive Password directives ignored.\n', LOG_WARNING)
  127.          ELSE
  128.             x := 9
  129.             WHILE (str[x] < 33) AND (str[x] <> 0)
  130.                INC x
  131.             ENDWHILE
  132.  
  133.             IF (str[x] <> 0)
  134.                password := String (EstrLen (str) - x)
  135.                StrCopy (password, str+x)
  136.             ENDIF
  137.          ENDIF
  138.       ELSE
  139.          log_message ('Unknown directive:  ', LOG_WARNING)
  140.          log_message (str, LOG_WARNING2)
  141.       ENDIF
  142.    ENDWHILE
  143.  
  144.    DisposeLink (str)
  145.    Close (config)
  146.  
  147.    IF (list_dir = NIL)
  148.       log_message ('No list directory specified!\n', LOG_ERROR)
  149.       RETURN
  150.    ENDIF
  151.  
  152.    /* Get our host name, if present */
  153.    str := String (256)
  154.    x := GetVar ('HOSTNAME', str, 255, GVF_GLOBAL_ONLY OR GVF_LOCAL_ONLY)
  155.    IF (x > 0)
  156.       hostname := str
  157.    ELSE
  158.       DisposeLink (str)
  159.    ENDIF
  160.  
  161.    /* Now load the list config files... */
  162.    get_list_attrs()
  163. ENDPROC
  164.  
  165.  
  166. /*
  167.  * Here's where we go through the ListDir directory and open all files ending in .list
  168.  */
  169. PROC get_list_attrs()
  170.    DEF fd, tmp:PTR TO config_node, old_dir, ap:PTR TO anchorpath, x, str:PTR TO CHAR
  171.  
  172.    old_dir := CurrentDir (list_dir)
  173.  
  174.    config_list := NIL
  175.  
  176.    NEW ap
  177.    IF (MatchFirst ('#?.list', ap))
  178.       log_message ('No lists to open.\n', LOG_ERROR)
  179.       END ap
  180.       CurrentDir (old_dir)
  181.       RETURN
  182.    ENDIF
  183.  
  184.    LOOP
  185.       fd := Open (ap.info.filename, MODE_OLDFILE)
  186.       IF (fd = NIL)
  187.          log_message ('Unable to open list file "', LOG_ERROR)
  188.          log_message (ap.info.filename, LOG_ERROR2)
  189.          log_message ('".\n', LOG_ERROR2)
  190.       ELSE
  191.          log_message ('Reading attributes for list "', LOG_INFO)
  192.          log_message (ap.info.filename, LOG_INFO2)
  193.          log_message ('".\n', LOG_INFO2)
  194.  
  195.          NEW tmp
  196. /* Need this here 'cause parse_list_attrs has to be able to check for a valid digest list */
  197.          tmp.next := config_list
  198.          tmp.name := String (StrLen (ap.info.filename)-5)
  199.          StrCopy (tmp.name, ap.info.filename)
  200.          LowerStr (tmp.name)
  201.          x := parse_list_attrs (fd, tmp)
  202.          IF (x)
  203.             str := String (80)
  204.             StringF (str, '#\d while reading list file "\s"\n', x, ap.info.filename)
  205.             log_message (str, LOG_ERROR)
  206.             DisposeLink (str)
  207.             END tmp
  208.          ELSE
  209.             config_list := tmp
  210.          ENDIF
  211.          Close (fd)
  212.  
  213.          /* Get the .issue overrides */
  214.          str := String (EstrLen (tmp.name) + 6)
  215.          StrCopy (str, tmp.name)
  216.          StrAdd (str, '.issue')
  217.          fd := Open (str, MODE_OLDFILE)
  218.          IF (fd)
  219.             parse_list_attrs (fd, tmp)
  220.             Close (fd)
  221.          ENDIF
  222.  
  223.          /* Get the user list */
  224.          SetStr (str, EstrLen (tmp.name))
  225.          StrAdd (str, '.who')
  226.          fd := Open (str, MODE_OLDFILE)
  227.          IF (fd)
  228.             get_who_list (fd, tmp)
  229.             Close (fd)
  230.          ELSE
  231.             log_message ('No who list available.\n', LOG_WARNING)
  232.          ENDIF
  233.  
  234.          DisposeLink (str)
  235.  
  236.          IF (check_list_attrs (tmp))
  237.             log_message ('List ignored.\n', LOG_ERROR)
  238.             config_list := tmp.next
  239.             tmp.next := NIL
  240.             free_config (tmp)
  241.          ENDIF
  242.       ENDIF
  243.  
  244.       IF (MatchNext (ap))
  245.          END ap
  246.          CurrentDir (old_dir)
  247.          RETURN
  248.       ENDIF
  249.    ENDLOOP
  250. /* We won't ever reach here */
  251. ENDPROC
  252.  
  253.  
  254. /*
  255.  * Parse the List Attributes in a file and place them in the provided struct
  256.  */
  257. PROC parse_list_attrs (fd, tmp:PTR TO config_node)
  258.    DEF str:PTR TO CHAR, x, holdover:PTR TO CHAR, tmp2
  259.  
  260.    str := String (256)
  261.    holdover := NIL
  262.    WHILE (Fgets (fd, str, 256))
  263.       IF (IF (holdover)  THEN (str[0] = "\a") ELSE FALSE)
  264.          /* Add onto the holdover string */
  265.          tmp2 := String (EstrLen (holdover) + StrLen (str) - 1)
  266.          StringF (tmp2, '\s\s', holdover, str+1)
  267.          DisposeLink (holdover)
  268.          holdover := tmp2
  269.       ELSE
  270.          IF (holdover)
  271.             set_list_attr (holdover, tmp)
  272.             DisposeLink (holdover)
  273.             holdover := NIL
  274.          ENDIF
  275.  
  276.          SetStr (str, StrLen (str))
  277.          IF (StrCmp (str, 'LongDesc:', 9) OR
  278.              StrCmp (str, 'DigestHeader:', 13) OR
  279.              StrCmp (str, 'DigestFooter:', 13) OR
  280.              StrCmp (str, 'Footer:', 7) OR
  281.              StrCmp (str, 'Headers:', 8) OR
  282.              StrCmp (str, 'Welcome:', 8) OR
  283.              StrCmp (str, 'KeepHeaders:', 12))
  284.             /* Possible multi-line */
  285.             holdover := String (EstrLen (str))
  286.             StrCopy (holdover, str)
  287.          ELSEIF (str[0] = "#") OR (str[0] < 33) OR (str[0] = "\a")
  288.             str[0] := 0       /* Comment */
  289.          ELSE
  290.             set_list_attr (str, tmp)
  291.          ENDIF
  292.       ENDIF
  293.    ENDWHILE
  294.  
  295.    IF (holdover)
  296.       set_list_attr (holdover, tmp)
  297.       DisposeLink (holdover)
  298.    ENDIF
  299. ENDPROC IoErr()
  300.  
  301.  
  302. /*
  303.  * Okay, so I pulled a cheezy move here, leveling this three deep...
  304.  *
  305.  * Sets an individual attribute.  Removes the trailing newline on most attrs.
  306.  */
  307. PROC set_list_attr (str:PTR TO CHAR, cfg:PTR TO config_node)
  308.    DEF str2:PTR TO CHAR, tmp:PTR TO LONG, str3:PTR TO CHAR, tmp2
  309.  
  310.    IF (StrLen (str) < 10)  THEN RETURN
  311.  
  312.    str2 := String (StrLen (str))
  313.    StrCopy (str2, str)
  314.    tmp := str2
  315.    str3 := find_empty (str2)
  316.    IF (str3 = NIL) AND (tmp[0] <> "CmpD")
  317.       /* Not a setting */
  318.       DisposeLink (str2)
  319.       RETURN
  320.    ENDIF
  321.  
  322.    str3[0] := 0
  323.    IF (tmp[0] <> "Foot")
  324.       str2 := TrimStr (str3+1)
  325.    ELSE
  326.       str2 := str3+1
  327.    ENDIF
  328.    IF (str2[0] = 0)
  329.       /* Empty setting */
  330.       DisposeLink (tmp)
  331.       RETURN
  332.    ENDIF
  333.  
  334.    tmp2 := tmp[0]
  335.    SELECT tmp2
  336.    CASE "Pass"
  337.       IF (tmp[1] = "word")
  338.          IF (cfg.password)  THEN DisposeLink (cfg.password)
  339.          cfg.password := String (StrLen (str2)-1)
  340.          StrCopy (cfg.password, str2)
  341.       ENDIF
  342.  
  343.    CASE "Arch"
  344.       IF (tmp[1] = "iveP")
  345.          IF (StrCmp (str2, 'none', 4))
  346.             cfg.archive_period := AP_NONE
  347.          ELSEIF (StrCmp (str2, 'daily', 5))
  348.             cfg.archive_period := AP_DAILY
  349.          ELSEIF (StrCmp (str2, 'weekly', 6))
  350.             cfg.archive_period := AP_WEEKLY
  351.          ELSEIF (StrCmp (str2, 'monthly', 7))
  352.             cfg.archive_period := AP_MONTHLY
  353.          ELSEIF (StrCmp (str2, 'yearly', 6))
  354.             cfg.archive_period := AP_YEARLY
  355.          ELSE
  356.             log_message ('Unknown value for ArchivePeriod.\n', LOG_WARNING)
  357.          ENDIF
  358.       ENDIF
  359.  
  360.    CASE "Auto"
  361.       IF (StrCmp (tmp+4, 'Cmd', 3))
  362.          IF (StrCmp (str2, 'fwd', 3))
  363.             cfg.flags := cfg.flags OR CNF_AUTOCMD_FWD
  364.          ELSEIF (StrCmp (str2, 'use', 3))
  365.             cfg.flags := cfg.flags OR CNF_AUTOCMD_USE
  366.          ENDIF
  367.       ENDIF
  368.  
  369.    CASE "Appr"
  370.       IF (tmp[1] = "oveP")
  371.          IF (cfg.approved)  THEN DisposeLink (cfg.approved)
  372.          cfg.approved := String (StrLen (str2)-1)
  373.          StrCopy (cfg.approved, str2)
  374.       ENDIF
  375.  
  376.    CASE "Shor"
  377.       IF (tmp[1] = "tDes")
  378.          IF (cfg.sdesc)  THEN DisposeLink (cfg.sdesc)
  379.          cfg.sdesc := String (StrLen (str2)-1)
  380.          StrCopy (cfg.sdesc, str2)
  381.       ENDIF
  382.  
  383.    CASE "Long"
  384.       IF (tmp[1] = "Desc")
  385.          IF (cfg.ldesc)  THEN DisposeLink (cfg.ldesc)
  386.          cfg.ldesc := String (StrLen (str2))
  387.          StrCopy (cfg.ldesc, str2)
  388.       ENDIF
  389.  
  390.    CASE "Foot"
  391.       IF (StrCmp (tmp+4, 'er', 2))
  392.          IF (cfg.footer)  THEN DisposeLink (cfg.footer)
  393.          cfg.footer := String (StrLen (str2))
  394.          StrCopy (cfg.footer, str2)
  395.       ENDIF
  396.  
  397.    CASE "Head"
  398.       IF (StrCmp (tmp+4, 'er', 2))
  399.          IF (cfg.header)  THEN DisposeLink (cfg.header)
  400.          cfg.header := String (StrLen (str2))
  401.          StrCopy (cfg.header, str2)
  402.       ENDIF
  403.  
  404.    CASE "CmpD"
  405.       IF (tmp[1] = "omai")
  406.          cfg.flags := cfg.flags OR CNF_CMP_DOMAIN
  407.       ENDIF
  408.  
  409.    CASE "Mode"
  410.       IF (tmp[1] = "rate")
  411.          IF (tmp[2] = "List")
  412.             tmp2 := CNF_MOD_LIST_SELF
  413.          ELSEIF (tmp[2] = "Post")
  414.             tmp2 := CNF_MOD_POSTS_SELF
  415.          ELSEIF (tmp[2] = "File")
  416.             tmp2 := CNF_MOD_FILES_SELF
  417.          ELSEIF (tmp[2] = "FPos")
  418.             tmp2 := CNF_MOD_FPOST_SELF
  419.          ELSEIF (tmp[2] = "Info")
  420.             tmp2 := CNF_MOD_INFO_SELF
  421.          ELSEIF (StrCmp (tmp+8, 'Who', 3))
  422.             tmp2 := CNF_MOD_WHO_SELF
  423.          ELSE
  424.             tmp2 := NIL
  425.          ENDIF
  426.  
  427.          IF (tmp2)
  428.             IF (StrCmp (str2, 'all', 3))
  429.                /* This is the default */
  430.             ELSEIF (StrCmp (str2, 'self', 4))
  431.                cfg.flags := cfg.flags OR tmp2
  432.             ELSEIF (StrCmp (str2, 'owner', 5))
  433.                cfg.flags := cfg.flags OR Shl (tmp2, 1)
  434.             ELSE
  435.                log_message ('Unknown value for Moderate.\n', LOG_WARNING)
  436.             ENDIF
  437.          ENDIF
  438.       ENDIF
  439.  
  440.    CASE "File"
  441.       IF (tmp[1] = "GetP")
  442.          IF (cfg.file_get_pwd)  THEN DisposeLink (cfg.file_get_pwd)
  443.          cfg.file_get_pwd := String (StrLen (str2)-1)
  444.          StrCopy (cfg.file_get_pwd, str2)
  445.       ELSEIF (tmp[1] = "PutP")
  446.          IF (cfg.file_put_pwd)  THEN DisposeLink (cfg.file_put_pwd)
  447.          cfg.file_put_pwd := String (StrLen (str2)-1)
  448.          StrCopy (cfg.file_put_pwd, str2)
  449.       ENDIF
  450.  
  451.    CASE "Owne"
  452.       IF (cfg.owner)  THEN DisposeLink (cfg.owner)
  453.       cfg.owner := String (StrLen (str2)-1)
  454.       StrCopy (cfg.owner, str2)
  455.  
  456.    CASE "Subj"
  457.       IF (cfg.subject)  THEN DisposeLink (cfg.subject)
  458.       cfg.subject := String (StrLen (str2) - 1)    /* Drop the \n */
  459.       StrCopy (cfg.subject, str2)
  460.  
  461.    CASE "Welc"
  462.       IF (cfg.welcome)  THEN DisposeLink (cfg.welcome)
  463.       cfg.welcome := String (StrLen (str2))
  464.       StrCopy (cfg.welcome, str2)
  465.  
  466.    CASE "Visi"
  467.       IF (StrCmp (str2, 'yes', 3))
  468.          /* Default */
  469.       ELSEIF (StrCmp (str2, 'no', 2))
  470.          cfg.flags := cfg.flags OR CNF_INVISIBLE
  471.       ELSE
  472.          log_message ('Unknown value for Visible.\n', LOG_WARNING)
  473.       ENDIF
  474.  
  475.    CASE "Decr"
  476.       IF (cfg.decrypt)  THEN DisposeLink (cfg.decrypt)
  477.       cfg.decrypt := String (StrLen (str2)-1)
  478.       StrCopy (cfg.decrypt, str2)
  479.  
  480.    CASE "Encr"
  481.       IF (cfg.encrypt)  THEN DisposeLink (cfg.encrypt)
  482.       cfg.encrypt := String (StrLen (str2)-1)
  483.       StrCopy (cfg.encrypt, str2)
  484.  
  485.    CASE "Keep"
  486.       IF (tmp[1] = "Rece")
  487.          IF (StrCmp (str2, 'yes', 3))
  488.             cfg.flags := cfg.flags OR CNF_KEEP_RECEIVED
  489.          ELSEIF (StrCmp (str2, 'no', 2))
  490.             /* Default */
  491.          ELSE
  492.             log_message ('Unknown value to KeepReceived.\n', LOG_WARNING)
  493.          ENDIF
  494.       ELSEIF (tmp[1] = "Head")
  495.          IF (StrCmp (str2, 'all', 3))
  496.             cfg.flags := cfg.flags OR CNF_KEEP_HEADERS
  497.          ELSEIF (StrCmp (str2, 'none', 4))
  498.             /* Default */
  499.          ELSE
  500.             /* List of headers */
  501.             IF (cfg.keep_headers)  THEN DisposeLink (cfg.keep_headers)
  502.             cfg.keep_headers := String (StrLen (str2))
  503.             StrCopy (cfg.keep_headers, str2)
  504.          ENDIF
  505.       ENDIF
  506.  
  507.    CASE "Requ"
  508.       IF (tmp[1] = "ireK")
  509.          IF (StrCmp (str2, 'yes', 3))
  510.             cfg.flags := cfg.flags OR CNF_REQUIRE_KEY
  511.          ELSEIF (StrCmp (str2, 'no', 2))
  512.             /* Default */
  513.          ELSE
  514.             log_message ('Unknown value for RequireKey.\n', LOG_WARNING)
  515.          ENDIF
  516.       ENDIF
  517.  
  518.    /* All Digest stuff here */
  519.    CASE "Dige"
  520.       IF (cfg.digest = NIL)  THEN NEW cfg.digest
  521.       IF (tmp[1] = "stNa")
  522.          IF (cfg.digest.name)  THEN DisposeLink (cfg.digest.name)
  523.          cfg.digest.name := String (StrLen (str2)-1)
  524.          StrCopy (cfg.digest.name, str2)
  525.       ELSEIF (tmp[1] = "stIs")
  526.          IF (StrCmp (tmp+8, 'sueName', 7))
  527.             IF (cfg.digest.iname)  THEN DisposeLink (cfg.digest.iname)
  528.             cfg.digest.iname := String (StrLen (str2)-1)
  529.             StrCopy (cfg.digest.iname, str2)
  530.          ELSEIF (StrCmp (tmp+8, 'sue', 3))
  531.             cfg.digest.issue := Val (str2)
  532.          ENDIF
  533.       ELSEIF (tmp[1] = "stHe")
  534.          IF (cfg.digest.header)  THEN DisposeLink (cfg.digest.header)
  535.          cfg.digest.header := String (StrLen (str2))
  536.          StrCopy (cfg.digest.header, str2)
  537.       ELSEIF (tmp[1] = "stFo")
  538.          IF (cfg.digest.footer)  THEN DisposeLink (cfg.digest.footer)
  539.          cfg.digest.footer := String (StrLen (str2))
  540.          StrCopy (cfg.digest.footer, str2)
  541.       ELSEIF (tmp[1] = "stSi")
  542.          cfg.digest.size := Val (str2)
  543.       ELSEIF (tmp[1] = "stLi")
  544.          cfg.digest.lines := Val (str2)
  545.       ELSEIF (tmp[1] = "stAg")
  546.          cfg.digest.age := Val (str2)
  547.       ELSEIF (tmp[1] = "stVo")
  548.          IF (StrCmp (tmp+8, 'lumeName', 8))
  549.             IF (cfg.digest.vname)  THEN DisposeLink (cfg.digest.vname)
  550.             cfg.digest.vname := String (StrLen (str2)-1)
  551.             StrCopy (cfg.digest.vname, str2)
  552.          ELSEIF (tmp[2] = "lume")
  553.             cfg.digest.volume := Val (str2)
  554.          ELSEIF (tmp[2] = "lIss")
  555.             cfg.digest.i_v := Val (str2)
  556.          ENDIF
  557.  
  558. /* These should ONLY be found in the .issue files! */
  559.       ELSEIF (tmp[1] = "stCu")
  560.          IF (tmp[2] = "Line")
  561.             cfg.digest.current_lines := Val (str2)
  562.          ELSEIF (tmp[2] = "Size")
  563.             cfg.digest.current_size := Val (str2)
  564.          ELSEIF (tmp[2] = "Date")
  565.             IF (cfg.digest.time = NIL)
  566.                cfg.digest.time := New (SIZEOF datetime)
  567.             ENDIF
  568.             IF (tmp[3] = "Days")
  569.                cfg.digest.time.stamp.days := Val (str2)
  570.             ELSEIF (tmp[3] = "Mins")
  571.                cfg.digest.time.stamp.minute := Val (str2)
  572.             ELSEIF (tmp[3] = "Tick")
  573.                cfg.digest.time.stamp.tick := Val (str2)
  574.             ENDIF
  575.          ENDIF
  576.       ENDIF
  577.  
  578.    DEFAULT
  579.       log_message ('Unknown option: ', LOG_WARNING)
  580.       log_message (str2, LOG_WARNING2)
  581.    ENDSELECT
  582.  
  583.    DisposeLink (tmp)
  584. ENDPROC
  585.  
  586.  
  587. /*
  588.  * Free up all memory allocated by this file
  589.  */
  590. PROC free_config (cfg=NIL)
  591.    DEF tmp:PTR TO config_node
  592.  
  593.    IF (cfg = NIL)
  594.       IF (list_dir)  THEN UnLock (list_dir)
  595.       IF (files_dir)  THEN UnLock (files_dir)
  596.       IF (password)  THEN DisposeLink (password)
  597.       cfg := config_list
  598.    ENDIF
  599.  
  600.    WHILE (cfg)
  601.       tmp := cfg
  602.       IF (tmp.name)  THEN DisposeLink (tmp.name)
  603.       IF (tmp.owner)  THEN DisposeLink (tmp.owner)
  604.       IF (tmp.password)  THEN DisposeLink (tmp.password)
  605.       IF (tmp.subject)  THEN DisposeLink (tmp.subject)
  606.       IF (tmp.approved)  THEN DisposeLink (tmp.approved)
  607.       IF (tmp.sdesc)  THEN DisposeLink (tmp.sdesc)
  608.       IF (tmp.ldesc)  THEN DisposeLink (tmp.ldesc)
  609.       IF (tmp.footer)  THEN DisposeLink (tmp.footer)
  610.       IF (tmp.header)  THEN DisposeLink (tmp.header)
  611.       IF (tmp.file_get_pwd)  THEN DisposeLink (tmp.file_get_pwd)
  612.       IF (tmp.file_put_pwd)  THEN DisposeLink (tmp.file_put_pwd)
  613.       IF (tmp.welcome)  THEN DisposeLink (tmp.welcome)
  614.       IF (tmp.decrypt)  THEN DisposeLink (tmp.decrypt)
  615.       IF (tmp.encrypt)  THEN DisposeLink (tmp.encrypt)
  616.       IF (tmp.keep_headers)  THEN DisposeLink (tmp.keep_headers)
  617.       IF (tmp.digest)
  618.          IF (tmp.digest.name)  THEN DisposeLink (tmp.digest.name)
  619.          IF (tmp.digest.header)  THEN DisposeLink (tmp.digest.header)
  620.          IF (tmp.digest.footer)  THEN DisposeLink (tmp.digest.footer)
  621.          IF (tmp.digest.iname)  THEN DisposeLink (tmp.digest.iname)
  622.          IF (tmp.digest.vname)  THEN DisposeLink (tmp.digest.vname)
  623.          IF (tmp.digest.time)  THEN Dispose (tmp.digest.time)
  624.          END tmp.digest
  625.       ENDIF
  626.       tmp := tmp.next
  627.       END cfg
  628.       cfg := tmp
  629.    ENDWHILE
  630. ENDPROC
  631.  
  632.  
  633. /*
  634.  * Find a list's config entry, or return NIL
  635.  */
  636. PROC find_list (str:PTR TO CHAR)
  637.    DEF tmp:PTR TO config_node
  638.  
  639.    IF (str = NIL)  THEN RETURN NIL
  640.    IF (str[0] = 0)  THEN RETURN NIL
  641.  
  642.    tmp := config_list
  643.    WHILE (tmp)
  644.       IF (StrCmp (str, tmp.name))  THEN RETURN tmp
  645.       tmp := tmp.next
  646.    ENDWHILE
  647. ENDPROC NIL
  648.  
  649.  
  650. /*
  651.  * Loads in a who list into a config node.
  652.  */
  653. PROC get_who_list (fd, cfg:PTR TO config_node)
  654.    DEF buff:PTR TO CHAR, str:PTR TO CHAR, str2
  655.  
  656.    buff := String (256)
  657.    WHILE (Fgets (fd, buff, 256))
  658.       str2 := TrimStr (buff)
  659.       IF (str2[0])
  660.          str := String (StrLen (str2)-1)     /* Drop \n */
  661.          StrCopy (str, str2)
  662.          cfg.users := add_link (cfg.users, str)
  663.       ENDIF
  664.    ENDWHILE
  665.  
  666.    DisposeLink (buff)
  667. ENDPROC
  668.  
  669. /*
  670.  * Checks validity of a list's configurations, and returns TRUE if something's wrong.
  671.  */
  672. PROC check_list_attrs (cfg:PTR TO config_node)
  673. /* Should check for the digest being a valid list, if present */
  674.    IF (cfg.owner = NIL) OR (cfg.password = NIL)  THEN RETURN TRUE
  675. ENDPROC FALSE
  676.  
  677.  
  678. /*
  679.  * Checks if a user is on a given list
  680.  *
  681.  * who is a List of E Strings
  682.  *
  683.  * flag should be (list.flags AND CNF_CMP_DOMAIN)
  684.  *
  685.  * Returns the string entry on the users list if it is found.
  686.  */
  687. PROC on_list (addr:PTR TO CHAR, who:PTR TO CHAR, flag)
  688.    DEF s1:PTR TO CHAR, s2:PTR TO CHAR, tmp
  689.  
  690.    s1 := String (80)
  691.    s2 := String (80)
  692.    StrCopy (s1, addr)
  693.    IF (flag)
  694.       tmp := InStr (addr, '@')
  695.       IF (tmp = -1)
  696.          tmp := InStr (addr, '!')
  697.          IF (tmp = -1)
  698.             /* Localhost */
  699.             StringF (s1, '\s@\s', addr, hostname)
  700.          ENDIF
  701.       ENDIF
  702.    ENDIF
  703.    IF (flag)  THEN domain_only (s1)
  704.  
  705.    WHILE (who)
  706.       StrCopy (s2, who)
  707.       IF (flag)
  708.          tmp := InStr (s2, '@')
  709.          IF (tmp = -1)
  710.             tmp := InStr (s2, '!')
  711.             IF (tmp = -1)
  712.                /* Localhost */
  713.                StringF (s2, '\s@\s', who, hostname)
  714.             ENDIF
  715.          ENDIF
  716.       ENDIF
  717.  
  718.       IF (StrCmp (s1, s2))
  719.          DisposeLink (s1)
  720.          DisposeLink (s2)
  721.          RETURN who
  722.       ENDIF
  723.       who := Next (who)
  724.    ENDWHILE
  725.  
  726.    DisposeLink (s1)
  727.    DisposeLink (s2)
  728. ENDPROC FALSE
  729.  
  730.  
  731. /*
  732.  * Removes the 'non-domain' portion of an address
  733.  */
  734. PROC domain_only (str:PTR TO CHAR)
  735.    DEF s:PTR TO CHAR, x, y, z
  736.  
  737.    s := String (EstrLen (str))
  738.    x := InStr (str, '@')
  739.    IF (x < 1)
  740.       /* Try UUCP name */
  741.       x := InStr (str, '!')
  742.       IF (x = -1)  THEN RETURN      /* No host address! */
  743.  
  744.       /* UUCP name */
  745.       /* I could be wrong about this formulae, but I have nothing to test it with */
  746.       y := x;  z := -1
  747.       WHILE ((x := InStr (str, '!', y+1)) > -1)
  748.          z := y;  y := x
  749.       ENDWHILE
  750.  
  751.       IF (z > -1) AND (y > z+1)
  752.          StrCopy (s, str+z+1)
  753.       ELSE
  754.          StrCopy (s, str)
  755.       ENDIF
  756.    ELSE
  757.       /* It's a little harder to piece together for normal Internet addresses... */
  758.       StrCopy (s, str, x+1)   /* Include the @ */
  759.       y := x;  z := -1
  760.       WHILE ((x := InStr (str, '.', y+1)) > -1)
  761.          z := y;  y := x
  762.       ENDWHILE
  763.  
  764.       IF (z > -1) AND (y > z+1)
  765.          StrAdd (s, str+z+1)
  766.       ELSE
  767.          StrAdd (s, y)
  768.       ENDIF
  769.    ENDIF
  770.  
  771.    StrCopy (str, s)
  772. ENDPROC
  773.  
  774.  
  775. /*
  776.  * Writes out a list's users to listname.who
  777.  */
  778. PROC write_user_list (list:PTR TO config_node)
  779.    DEF s:PTR TO CHAR, fd, s2:PTR TO CHAR, old_dir
  780.  
  781.    old_dir := CurrentDir (list_dir)
  782.  
  783.    s := String (80)
  784.    StrCopy (s, list.name)
  785.    StrAdd (s, '.who')
  786.    fd := Open (s, MODE_NEWFILE)
  787.    IF (fd)
  788.       /* write who list */
  789.       s2 := list.users
  790.       WHILE (s2)
  791.          Fwrite (fd, s2, 1, EstrLen (s2))
  792.          FputC (fd, "\n")
  793.          s2 := Next (s2)
  794.       ENDWHILE
  795.       Close (fd)
  796.    ELSE
  797.       log_message ('Not able to write who list for "', LOG_WARNING)
  798.       log_message (s, LOG_WARNING2)
  799.       log_message ('".\n', LOG_WARNING2)
  800.       CurrentDir (old_dir)
  801.       RETURN FALSE
  802.    ENDIF
  803.    DisposeLink (s)
  804.    CurrentDir (old_dir)
  805. ENDPROC TRUE
  806.  
  807.  
  808. /*
  809.  * Writes out the current digest issue information.
  810.  */
  811. PROC write_issue (list:PTR TO config_node)
  812.    DEF str:PTR TO CHAR, fd, str2:PTR TO CHAR, old_dir
  813.  
  814.    str := String (EstrLen (list.name) + 6)
  815.    StringF (str, '\s.issue', list.name)
  816.    old_dir := CurrentDir (list_dir)
  817.  
  818.    fd := Open (str, MODE_NEWFILE)
  819.    IF (fd = NIL)
  820.       log_message ('Unable to write issue information for list "', LOG_ERROR)
  821.       log_message (list.name, LOG_ERROR2)
  822.       log_message ('".\n', LOG_ERROR2)
  823.       DisposeLink (str)
  824.       CurrentDir (old_dir)
  825.       RETURN
  826.    ENDIF
  827.  
  828.    str2 := String (80)
  829.    StringF (str2, 'DigestIssue: \d\n', list.digest.issue)
  830.    Fputs (fd, str2)
  831.  
  832.    IF (list.digest.volume)
  833.       StringF (str2, 'DigestVolume: \d\n', list.digest.volume)
  834.       Fputs (fd, str2)
  835.    ENDIF
  836.  
  837.    StringF (str2, 'DigestCuLine: \d\n', list.digest.current_lines)
  838.    Fputs (fd, str2)
  839.  
  840.    StringF (str2, 'DigestCuSize: \d\n', list.digest.current_size)
  841.    Fputs (fd, str2)
  842.  
  843.    IF (list.digest.time)
  844.       StringF (str2, 'DigestCuDateDays: \d\n', list.digest.time.stamp.days)
  845.       Fputs (fd, str2)
  846.  
  847.       StringF (str2, 'DigestCuDateMins: \d\n', list.digest.time.stamp.minute)
  848.       Fputs (fd, str2)
  849.  
  850.       StringF (str2, 'DigestCuDateTick: \d\n', list.digest.time.stamp.tick)
  851.       Fputs (fd, str2)
  852.    ENDIF
  853.  
  854.    Close (fd)
  855.    CurrentDir (old_dir)
  856.    DisposeLink (str2)
  857.    DisposeLink (str)
  858. ENDPROC
  859.  
  860.  
  861. /*
  862.  * Low-level loads in a file as a chain of E Strings.
  863.  */
  864. PROC load_in_file (fd)
  865.    DEF str:PTR TO CHAR, buff:PTR TO CHAR, head
  866.  
  867.    str := head := NIL
  868.    buff := String (256)
  869.    WHILE (Fgets (fd, buff, 256))
  870.       str := String (StrLen (buff))
  871.       StrCopy (str, buff)
  872.       head := add_link (head, str)
  873.    ENDWHILE
  874. ENDPROC head
  875.